package com.ruoyi.web.controller.dj;

import cn.hutool.core.lang.Tuple;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.ProcessStopException;
import com.ruoyi.common.utils.ExecUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.SseEmitterUtils;
import com.ruoyi.common.utils.bean.ObjUtil;
import com.ruoyi.common.utils.biz.BizModule;
import com.ruoyi.common.utils.biz.CommonUtil;
import com.ruoyi.common.utils.biz.PathUtil;
import com.ruoyi.common.utils.biz.ResultSaveModule;
import com.ruoyi.web.server.entity.ResultSave;
import com.ruoyi.web.server.service.ResultSaveService;
import com.ruoyi.web.vo.WindModelReviseCalVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * <p>
 *  多模型计算与预报-模型修订计算
 * </p>
 *
 * @author liuhl
 * @since 2024-10-25
 */
@RestController
@RequestMapping("/dj/model")
@AllArgsConstructor
@Slf4j
@Api(tags = "多模型计算与预报")
public class ModelReviseController {

    private ResultSaveService resultSaveService;


    @PostMapping("/revise/cal")
    @ApiOperation(value = "模型修订计算", notes = "根据页面入参计算出结果文件或者数据", httpMethod = "POST")
    public AjaxResult modelReviseCal(@RequestBody WindModelReviseCalVo vo) throws ProcessStopException {

        String type = vo.getType();

        String configSetPath = PathUtil.commonIniPath();
        String outputPath = PathUtil.outputModuleDir(BizModule.MODEL_REVISE);
        String collectResultPath = PathUtil.outputModuleDir(BizModule.COLLECT) + PathUtil.moduleDirs().getCollect().get("result");
        String logPath = PathUtil.tmpModuleDir(BizModule.MODEL_CAL);

        String command = "";

        //卫星
        if ("satellite".equals(type)){

            String codePath = PathUtil.codeModuleDir(BizModule.MODEL_CAL) + PathUtil.moduleDirs().getModelRevise().get("model_revise_sate");
            int subIndex = codePath.lastIndexOf("/");
            String dirPath = codePath.substring(0, subIndex);
            String fileName = codePath.substring(subIndex+1);
            codePath = fileName;

            String dataName = vo.getSateName();
            String dataType = StrUtil.isBlank(vo.getDataType())?"None":vo.getDataType();
            String period = vo.getPeriod();
            String time = vo.getTime();
            String resultType = vo.getResultType();
            String area = CommonUtil.getArea(vo.getLongitudeRange(), vo.getLatitudeRange());
            String level = ("horizon".equals(resultType) || "trajectory".equals(resultType))?vo.getLevel():"all";
            String resultResolution = "[" + vo.getResultResolution() + "]";
            String originInsertMethod = vo.getOriginInsertMethod();
            String resultInsertMethod = vo.getResultInsertMethod();

            int[] smallDis = new int[]{20,10,1,0,0,1};
            if ("point_profile".equals(resultType)){
                String[] split = vo.getSpan().split(",");
                smallDis[2] = Integer.valueOf(split[0]);
                smallDis[5] = Integer.valueOf(split[1]);
            }

            //Avg_Model_Revision(setfile, filepath_character, filepath_obs, fileout, filelog, dataname, data_type,
            //             model_revision_time, predict, result_type, point, high, radio, inter_type, inter_type_correct, small_dis)
            command = "cd "+dirPath+" && python -u " + codePath+" "+configSetPath+" "+outputPath+" "+collectResultPath+" "+outputPath+" "+logPath+" "+dataName+" "+dataType+" "
                    +period +" "+time+" "+resultType+" "+area+" "+level+" "+resultResolution+" "+originInsertMethod+" "+resultInsertMethod+" "+ JSONUtil.toJsonStr(smallDis);

        }

        //数据融合
        if ("fusion".equals(type)){


            String codePath = PathUtil.codeModuleDir(BizModule.MODEL_CAL) + PathUtil.moduleDirs().getModelRevise().get("model_revise_fusion");
            int subIndex = codePath.lastIndexOf("/");
            String dirPath = codePath.substring(0, subIndex);
            String fileName = codePath.substring(subIndex+1);
            codePath = fileName;

            // 用一个下划线归约后再用,分隔
            // Nimbus-7_None_daily,TIMED_S0450_month_average,UARS_A_month_average
            String sateName = vo.getSateName();
            String dataType = vo.getDataType();
            String dailyMonthValue = vo.getDailyMonthValue();

            List<String> sateNameList = Arrays.asList(sateName.split(","));
            List<String> dataTypeList = Arrays.asList(dataType.split(","));
            List<String> dailyMonthValueList = Arrays.asList(dailyMonthValue.split(","));

            dataType = IntStream.range(0, sateNameList.size()).mapToObj(index -> {
                return sateNameList.get(index) + "_" + dataTypeList.get(index) + "_" + dailyMonthValueList.get(index);
            }).collect(Collectors.joining(","));


            String dataName = vo.getFusionMethod();

            String period = vo.getPeriod();
            String time = vo.getTime();
            String resultType = vo.getResultType();
            String area = CommonUtil.getArea(vo.getLongitudeRange(), vo.getLatitudeRange());
            String level = ("horizon".equals(resultType) || "trajectory".equals(resultType))?vo.getLevel():"all";
            String resultResolution = vo.getResultResolution();
            String originInsertMethod = vo.getOriginInsertMethod();
            String resultInsertMethod = vo.getResultInsertMethod();

            int[] smallDis = new int[]{20,10,1,0,0,1};
            if ("point_profile".equals(resultType)){
                String[] split = vo.getSpan().split(",");
                smallDis[2] = Integer.valueOf(split[0]);
                smallDis[5] = Integer.valueOf(split[1]);
            }

            //    Avg_Model_Revision(setfile, filepath_obs, fileout, filelog, dataname, data_type,
            //             model_revision_time, predict, result_type, point, high, radio, inter_type, inter_type_correct, small_dis)
            command = "cd "+dirPath+" && python -u " + codePath+" "+configSetPath+" "+collectResultPath+" "+outputPath+" "+logPath+" "+dataName+" "+dataType+" "
                    +period +" "+time+" "+resultType+" "+area+" "+level+" "+resultResolution+" "+originInsertMethod+" "+resultInsertMethod+" "+JSONUtil.toJsonStr(smallDis);

        }

        //雷达
        if ("radar".equals(type)){

            String codePath = PathUtil.codeModuleDir(BizModule.MODEL_CAL) + PathUtil.moduleDirs().getModelRevise().get("model_revise_radar");
            int subIndex = codePath.lastIndexOf("/");
            String dirPath = codePath.substring(0, subIndex);
            String fileName = codePath.substring(subIndex+1);
            codePath = fileName;

            String dataName = vo.getSateName();
            String period = vo.getPeriod();
            String time = vo.getTime();
            String timeUnit = vo.getTimeUnit();
            String resultResolution = vo.getResultResolution();
            int[] smallDis = new int[]{20,10,1,0,0,1};
            String[] split = vo.getSpan().split(",");
            smallDis[2] = Integer.valueOf(split[0]);
            smallDis[5] = Integer.valueOf(split[1]);

            //Radar_Avg_Model_Revision(setfile, filepath_character, filepath_obs, fileout, filelog, dataname,
            //             model_revision_time, predict, time_type, radio, small_dis)
            command = "cd "+dirPath+" && python -u " + codePath+" "+configSetPath+" "+outputPath+" "+collectResultPath+" "+outputPath+" "+logPath+" "+dataName+" "+period+" "
                    +time +" "+timeUnit+" "+resultResolution+" "+JSONUtil.toJsonStr(smallDis);

        }

        String sseId = vo.getSseId()+ SecurityUtils.getLoginUser().getUsername();
        String results = ExecUtil.execPython(Stream.of("bash", "-c",command).collect(Collectors.toList()), sseId);

        SseEmitterUtils.sendMessage(sseId, "100");
        SseEmitterUtils.remove(sseId);

        if (results == null || !results.contains("###")){
            return AjaxResult.error(results);
        }

        String[] splitArr = results.split("###");
        //如果结果类型是轨迹，则是数据，其他的话就是png。
        Map<String,Object> resultMap = new LinkedHashMap<>();
        String log = splitArr[0];
        if (StrUtil.isNotBlank(log)){
            resultMap.put("log", log);
        }

        if ("trajectory".equals(vo.getResultType())){
            String jsonStr = splitArr[1];
            resultMap.put("data", jsonStr);
            resultMap.put("pic", null);
            String picJsonStr = splitArr[2];
            if (StrUtil.isNotBlank(picJsonStr) && !"null".equalsIgnoreCase(picJsonStr)){

                String delimiter = ">>>";
                Map<String,String> picPathMap = JSONUtil.toBean(picJsonStr, Map.class);
                Map<String,Map<String,String>> groupMap = new HashMap<>();

                picPathMap.entrySet().stream().forEach(entry -> {
                    String key = entry.getKey();
                    String value = entry.getValue();
                    String[] split = key.split(delimiter);
                    String key1 = split[0];
                    String key2 = split[1];

                    Map<String, String> subgroupMap = groupMap.get(key1);
                    if (subgroupMap == null){
                        subgroupMap = new HashMap<>();
                        subgroupMap.put(key2, value);
                        groupMap.put(key1, subgroupMap);
                    }else {
                        subgroupMap.put(key2, value);
                    }
                });
                resultMap.put("pic", groupMap);

                resultMap.put("resultData", JSONUtil.toJsonStr(picPathMap.values()));
                resultMap.put("module", ResultSaveModule.MODEL_CAL);
                resultMap.put("submodule", ResultSaveModule.MODEL_CAL_REVISE);

            }
            return AjaxResult.success(resultMap);
        }else {
            String data = splitArr[1];
            if (splitArr.length > 2){
                String extraData = splitArr[2];
                resultMap.put("extraData",extraData); //2024-9-11 新增表格数据
            }

            //data:
            //{
            //    "u>>>revised": 		"path1",
            //    "u>>>obs": 			"path2",
            //    "Error_u>>>HWM07": 	"path3",
            //    "v>>>Avg model": 	    "path4"
            //}

            //给前端的数据格式：
            //{
            //	"x":[1,2,3,4],
            //	"y":[a,b,c],
            //	data:[[path1,path2," ",path3],[path1,path2,path3," "],[path1," ",path2,path3]]
            //}

            Map<String,String> pathMap = JSONUtil.toBean(data, Map.class);
            String delimiter = ">>>";
            // 整理出 x ,y
            List<Tuple> unsortedXAndYTupleList = pathMap.entrySet().stream().map(entry -> {
                String[] split = entry.getKey().split(delimiter);
                return new Tuple(split[0], split[1]);
            }).collect(Collectors.toList());

            //先按照长度升序
            //各个长度组内再降序
            List<Tuple> xAndYTupleList = unsortedXAndYTupleList.stream().sorted(
                            Comparator.comparingInt((Tuple o) -> o.get(0).toString().length())
                                    .thenComparing((Tuple a) -> a.get(0).toString() ,Comparator.reverseOrder())
                    )
                    .collect(Collectors.toList());
            List<String> xList = xAndYTupleList.stream().map(o -> o.get(1).toString()).distinct().collect(Collectors.toList());
            List<String> yList = xAndYTupleList.stream().map(o -> o.get(0).toString()).distinct().collect(Collectors.toList());
            List<List<String>> dataList = yList.stream()
                    .map(y -> {
                        return xList.stream().map(x -> {
                            String path = pathMap.get(y + delimiter + x);
                            if (StrUtil.isBlank(path)) {
                                path = "";
                            }
                            return path;
                        }).collect(Collectors.toList());
                    }).collect(Collectors.toList());
            resultMap.put("x",xList);
            resultMap.put("y",yList);
            resultMap.put("data",dataList);


            List<String> list = dataList.stream().flatMap(Collection::stream).filter(StrUtil::isNotBlank).collect(Collectors.toList());
            List<String> fileNameList = list
                    .stream()
                    .map(filePath -> filePath.substring(filePath.lastIndexOf("/") + 1))
                    .collect(Collectors.toList());
            String fileNames = JSONUtil.toJsonStr(fileNameList);

            ResultSave resultSave = ResultSave.builder()
                    .params(ObjUtil.toStr(vo))
                    .resultData(JSONUtil.toJsonStr(list))
                    .fileNames(fileNames)
                    .module(ResultSaveModule.MODEL_CAL)
                    .submodule(ResultSaveModule.MODEL_CAL_REVISE)
                    .createTime(new Date())
                    .build();

            resultMap.put("resultData", JSONUtil.toJsonStr(list));
            resultMap.put("module", ResultSaveModule.MODEL_CAL);
            resultMap.put("submodule", ResultSaveModule.MODEL_CAL_REVISE);

            resultSaveService.save(resultSave);
            return AjaxResult.success(resultMap);
        }
    }

}
